package com.example.config;

import com.example.authentication.OAuth2ConfigurerUtils;
import com.example.authentication.PasswordCredentialsAuthenticationConverter;
import com.example.authentication.PasswordCredentialsAuthenticationProvider;
import com.example.feign.UsernameFeignClient;
import com.example.handler.ThirdOauth2AuthenticationSuccessHandler;
import com.example.login.LoginFailHandler;
import com.example.login.UsernamePasswordLoginSuccessHandler;
import com.example.login.username.UsernameAuthenticationFilter;
import com.example.login.username.UsernameAuthenticationProvider;
import com.example.redis.CaptchaRedis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;

import java.util.List;

/**
 * authorization_code授权要求资源所有者进行身份验证。因此，
 * 除了默认的 OAuth2 安全配置之外，还必须配置用户身份验证机制。
 */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(jsr250Enabled = true, securedEnabled = true)
public class AuthorizationServerSecurityConfig {

//    private final ApplicationContext applicationContext;
//
//    public AuthorizationServerSecurityConfig(ApplicationContext applicationContext) {
//        this.applicationContext = applicationContext;
//    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    /**
     * 为客户端密钥配置明文密码编码器
     * @return 明文密码编码器实例
     */
//    @Bean("clientPasswordEncoder")
//    public PasswordEncoder clientPasswordEncoder() {
//        return NoOpPasswordEncoder.getInstance();
//    }

    @Autowired
    private CaptchaRedis captchaRedis;

    @Autowired
    private ThirdOauth2AuthenticationSuccessHandler thirdOauth2AuthenticationSuccessHandler;

    @Autowired
    @Qualifier("UserDetailsServiceImpl")
    private UserDetailsService userDetailsService;

    @Autowired
    @Qualifier("BusinessUserDetailsServiceImpl")
    private UserDetailsService businessUserDetailsService;

    @Autowired
    private UsernameFeignClient usernameFeignClient;

    @Autowired
    private UsernamePasswordLoginSuccessHandler usernamePasswordLoginSuccessHandler;

    @Autowired
    private LoginFailHandler loginFailHandler;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Bean
    public UsernameAuthenticationProvider usernameAuthenticationProvider(){
        return new UsernameAuthenticationProvider(userDetailsService, passwordEncoder(), jdbcTemplate);
    }


    /**
     * 协议端点
     */
    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
            throws Exception {
        //默认的设置，点击源码其实就是配置了认证端点的csrf校验

        //便捷的默认安全配置，默认的设置，点击源码其实就是配置了认证端点的csrf校验
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
//       1. 配置了OAuth 2.0授权服务器，包括令牌和授权端点的处理逻辑。
        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                .oidc(Customizer.withDefaults())   // Enable OpenID Connect 1.0
                .tokenEndpoint(endpointConfigurer -> endpointConfigurer.authenticationProviders(authenticationProviders
                                -> authenticationProviders.add(new PasswordCredentialsAuthenticationProvider(
                                OAuth2ConfigurerUtils.getAuthorizationService(http)
                                , OAuth2ConfigurerUtils.getTokenGenerator(http)
                                , userDetailsService
                                , businessUserDetailsService
                                , usernameFeignClient
                                , passwordEncoder()
                        )))
                        // AuthenticationProvider AuthenticationConverter 访问令牌的转换，获取过密码凭证方式获取访问令牌
                        .accessTokenRequestConverters(authenticationConverters -> authenticationConverters.add(new PasswordCredentialsAuthenticationConverter())));
        //授权端点
        // 设置了授权端点的同意页面路径为/oauth2/consent，这意味着当用户需要同意授权请求时，会被重定向到这个页面。
//                .authorizationEndpoint(endpoint -> {
//                    //配置自定义的授权页面
//                    endpoint.consentPage("/oauth2/consent");
//                });

        http
                // Redirect to the login page when not authenticated from the
                // authorization endpoint
                // 未登录时访问认证端点将被重定向至login页面
                // 2. 添加了异常处理机制，确保未认证的用户能被正确地重定向至登录页面。
                .exceptionHandling((exceptions) -> exceptions
                        .defaultAuthenticationEntryPointFor(
                                new LoginUrlAuthenticationEntryPoint("/login"),
                                new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                        )
                )
                .oauth2ResourceServer((resourceServer) -> resourceServer.jwt(Customizer.withDefaults()));
        return http.build();
    }

    /**
     * 用于身份验证的 Spring Security 过滤器链。
     *
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    @Order(2) // 指定 Bean 的加载顺序 用于指定不同 SecurityFilterChain 的执行顺序 值越小，优先级越高。
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
            throws Exception {
        http.csrf(AbstractHttpConfigurer::disable)
                .formLogin(AbstractHttpConfigurer::disable)
                .cors(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests((authorize) -> authorize
                        //放行自定义的登录接口及静态资源文件
                        .requestMatchers("/oauth2/authorize").authenticated()
                        .requestMatchers("/oauth2/token").permitAll()
                        .requestMatchers("/webchat/login").permitAll()
                        .requestMatchers("/login", "/oauth2/consent", "/assets/**", "/markdown/**").permitAll()
                        .requestMatchers("/captcha", "/captcha/**").permitAll() // 请求没有放行的自动302跳转登录页面
                        .requestMatchers("/user/**").permitAll()
                        .requestMatchers("/getNewUuid").permitAll()
                        .requestMatchers("/public/**", "/gitee-callback", "/callback", "/meassage-callback").permitAll()
                        .requestMatchers("/admin/**").permitAll()
                        .requestMatchers("/captchab/**").permitAll()
                        .requestMatchers("/registerclient/**").permitAll()

                        //  允许访问/doc.html以及所有openapi 3相关的路径
                        .requestMatchers(
                                new AntPathRequestMatcher("/favicon.ico"),
                                new AntPathRequestMatcher("/doc.html"),
                                new AntPathRequestMatcher("/v3/api-docs/**"),
                                new AntPathRequestMatcher("/webjars/**")
                        ).permitAll()
                        .requestMatchers("/login/oauth2/code/gitee").permitAll() // callback
                        .requestMatchers("/oauth2/authorization/gitee").permitAll() // 登录
                        .requestMatchers("/redirect_uris/callback").permitAll()
                        .anyRequest().authenticated()
                )
//                .formLogin(configurer -> {
//                    //配置自定义的登录界面
//                    configurer.loginPage("/login"); // .loginProcessingUrl("/user/login/username")
////                            .successHandler(customAuthenticationSuccessHandler) // 登录成功后跳转的页面
////                            .failureUrl("/login?error");
//                })
                .oauth2Login(login -> login.loginPage("/login")
                        .successHandler(thirdOauth2AuthenticationSuccessHandler) // 登录成功后跳转的页面
                        .failureHandler(loginFailHandler));

        // 加一个登录方式。用户名、密码登录

        http.addFilterBefore(usernameAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    /**
     * oauth2登录接口
     * @return
     */
    @Bean
    public UsernameAuthenticationFilter usernameAuthenticationFilter() {
        return new UsernameAuthenticationFilter(
                new AntPathRequestMatcher("/user/login/username", HttpMethod.POST.name()),
                new ProviderManager(List.of(usernameAuthenticationProvider())),
                usernamePasswordLoginSuccessHandler,
                loginFailHandler,
                captchaRedis);
    }
    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder().build();
    }

}
